home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Tools / Languages / MacGambit 2.0 / sources2 / Thomas / generic.scm < prev    next >
Encoding:
Text File  |  1992-11-25  |  22.1 KB  |  482 lines  |  [TEXT/gamI]

  1. ;*              Copyright 1992 Digital Equipment Corporation
  2. ;*                         All Rights Reserved
  3. ;*
  4. ;* Permission to use, copy, and modify this software and its documentation is
  5. ;* hereby granted only under the following terms and conditions.  Both the
  6. ;* above copyright notice and this permission notice must appear in all copies
  7. ;* of the software, derivative works or modified versions, and any portions
  8. ;* thereof, and both notices must appear in supporting documentation.
  9. ;*
  10. ;* Users of this software agree to the terms and conditions set forth herein,
  11. ;* and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
  12. ;* right and license under any changes, enhancements or extensions made to the
  13. ;* core functions of the software, including but not limited to those affording
  14. ;* compatibility with other hardware or software environments, but excluding
  15. ;* applications which incorporate this software.  Users further agree to use
  16. ;* their best efforts to return to Digital any such changes, enhancements or
  17. ;* extensions that they make and inform Digital of noteworthy uses of this
  18. ;* software.  Correspondence should be provided to Digital at:
  19. ;*
  20. ;*            Director, Cambridge Research Lab
  21. ;*            Digital Equipment Corp
  22. ;*            One Kendall Square, Bldg 700
  23. ;*            Cambridge MA 02139
  24. ;*
  25. ;* This software may be distributed (but not offered for sale or transferred
  26. ;* for compensation) to third parties, provided such third parties agree to
  27. ;* abide by the terms and conditions of this notice.
  28. ;*
  29. ;* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
  30. ;* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
  31. ;* MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
  32. ;* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  33. ;* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
  34. ;* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  35. ;* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  36. ;* SOFTWARE.
  37.  
  38. ; $Id: generic.scm,v 1.30 1992/09/21 21:27:28 birkholz Exp $
  39.  
  40. ;;;;  Generic Operation Dispatch Mechanism
  41.  
  42. ;;; Uses MIT Scheme 1d-tables to implement method -> param-list
  43. ;;; table lookup.  Portability requires renaming from "1d" to "oned"
  44.  
  45. ;;;; Methods
  46.  
  47. (define param-list-type
  48.   (make-record-type 'dylan-parameter-list
  49.             '(nrequired specializers next? rest? keys)))
  50. (define param-list? (record-predicate param-list-type))
  51. (define param-list.nrequired (record-accessor param-list-type 'nrequired))
  52. (define param-list.specializers (record-accessor param-list-type 'specializers))
  53. (define param-list.next? (record-accessor param-list-type 'next?))
  54. (define param-list.rest? (record-accessor param-list-type 'rest?))
  55. (define param-list.keys (record-accessor param-list-type 'keys))
  56.  
  57. (define make-param-list
  58.   (let ((makeit (record-constructor param-list-type)))
  59.     (lambda (required next? rest? keys)
  60.       (let ((required (guarantee-symbol-and-specializer-list required)))
  61.     (makeit (length required)
  62.         (map cadr required)
  63.         (guarantee-boolean next?)
  64.         (guarantee-boolean rest?)
  65.         (guarantee-keys keys))))))
  66.  
  67. (define (guarantee-symbol-and-specializer-list original-list)
  68.   (if (all? (lambda (elt)
  69.           (and (pair? elt) (pair? (cdr elt)) (null? (cddr elt))
  70.            (symbol? (car elt))
  71.            (or (class? (cadr elt))
  72.                (singleton? (cadr elt)))))
  73.         original-list)
  74.       original-list
  75.       (guarantee-symbol-and-specializer-list
  76.        (dylan-call dylan:error
  77.            "invalid param-list"
  78.            original-list))))
  79.  
  80. (define (guarantee-keys keys)
  81.   (cond ((not keys) #F)
  82.     ((not (pair? keys)) '#())
  83.     ((all? keyword? keys) keys)
  84.     (else (guarantee-keys
  85.            (dylan-call dylan:error
  86.                "invalid keywords" keys)))))
  87.  
  88. (define (guarantee-boolean bool)
  89.   (if bool #T #F))
  90.  
  91. (define (guarantee-integer object)
  92.   (if (integer? object)
  93.       object
  94.       (guarantee-integer (dylan-call dylan:error "not an integer" object))))
  95.  
  96. (define *method-data* (make-OneD-table))
  97.  
  98. (define (method-data method)
  99.   (let ((data (OneD-table/get *method-data* method #F)))
  100.     (if (not data)
  101.     (dylan-call dylan:error "not a method" method)
  102.     data)))
  103.  
  104. (define (method.param-list method)
  105.   (method-data method))
  106.  
  107. (define (method.specializers method)
  108.   (param-list.specializers (method-data method)))
  109.  
  110. (define (method.nrequired method)
  111.   (param-list.nrequired (method-data method)))
  112.  
  113. (define (method.rest? method)
  114.   (param-list.rest? (method-data method)))
  115.  
  116. (define (method.keys method)
  117.   (param-list.keys (method-data method)))
  118.  
  119. (define (dylan::method? method)
  120.   (if (OneD-table/get *method-data* method #F)
  121.       #T
  122.       #F))
  123.  
  124. (define (dylan::make-method param-list method)
  125.   (OneD-table/put! *method-data* method (guarantee-param-list param-list))
  126.   method)
  127.  
  128. (define (guarantee-param-list param-list)
  129.   (if (param-list? param-list)
  130.       param-list
  131.       (guarantee-param-list
  132.        (dylan-call dylan:error "not a param-list" param-list))))
  133.  
  134. ;;;; Generic Functions
  135.  
  136. (define generic-function-data-type
  137.   (make-record-type 'dylan-generic-function-data
  138.             '(name nrequired keys rest? methods read-only?)))
  139.  
  140. (define generic-function-data.nrequired
  141.   (record-accessor generic-function-data-type 'nrequired))
  142. (define generic-function-data.keys
  143.   (record-accessor generic-function-data-type 'keys))
  144. (define generic-function-data.rest?
  145.   (record-accessor generic-function-data-type 'rest?))
  146. (define generic-function-data.methods
  147.   (record-accessor generic-function-data-type 'methods))
  148. (define set-generic-function-data.methods!
  149.   (record-updater generic-function-data-type 'methods))
  150. (define generic-function-data.read-only?
  151.   (record-accessor generic-function-data-type 'read-only?))
  152. (define set-generic-function-data.read-only?!
  153.   (record-updater generic-function-data-type 'read-only?))
  154. (define make-generic-function-data
  155.   (record-constructor generic-function-data-type))
  156.  
  157. (define *generic-function-data* (make-OneD-table))
  158.  
  159. (define (generic-function-data fn)
  160.   (let ((data (OneD-table/get *generic-function-data* fn #F)))
  161.     (if (not data)
  162.     (dylan-call dylan:error "not a generic function" fn)
  163.     data)))
  164.  
  165. (define (generic-function.nrequired generic-function)
  166.   (generic-function-data.nrequired (generic-function-data generic-function)))
  167.  
  168. (define (generic-function.keys generic-function)
  169.   (generic-function-data.keys (generic-function-data generic-function)))
  170.  
  171. (define (generic-function.rest? generic-function)
  172.   (generic-function-data.rest? (generic-function-data generic-function)))
  173.  
  174. (define (generic-function.methods generic-function)
  175.   (generic-function-data.methods (generic-function-data generic-function)))
  176.  
  177. (define (generic-function.read-only? generic-function)
  178.   (generic-function-data.read-only? (generic-function-data generic-function)))
  179.  
  180. (define (set-generic-function.read-only?! generic-function read-only?)
  181.   (set-generic-function-data.read-only?!
  182.    (generic-function-data generic-function) (if read-only? #T #F)))
  183.  
  184. (define (find-method generic-function specializers)
  185.   (let loop ((methods (generic-function.methods generic-function)))
  186.     (if (pair? methods)
  187.     (if (specializers=? specializers
  188.                 (method.specializers (car methods)))
  189.         (car methods)
  190.         (loop (cdr methods)))
  191.     #F)))
  192.  
  193. (define delete-method!
  194.   (letrec ((delete-pair!
  195.         (lambda (pair list)
  196.           (if (eq? pair list)
  197.           (cdr list)
  198.           (let loop ((pairs list))
  199.             (if (pair? pairs)
  200.             (if (eq? pair (cdr pairs))
  201.                 (begin 
  202.                   (set-cdr! pairs (cddr pairs))
  203.                   list)
  204.                 (loop (cdr pairs)))
  205.             list))))))
  206.     (lambda (generic-function method)
  207.       (let* ((data (generic-function-data generic-function))
  208.          (pair (memq method (generic-function-data.methods data))))
  209.     (if (not pair)
  210.         (dylan-call dylan:error
  211.             "method not in generic function"
  212.             method generic-function)
  213.         (set-generic-function-data.methods!
  214.          data (delete-pair! pair (generic-function-data.methods data)))))
  215.       method)))
  216.  
  217. (define (Add-Method generic-function method . multi-value-receiver)
  218.   (let ((data (generic-function-data generic-function))
  219.     (param-list (method.param-list method)))
  220.     (define (congruency-error)
  221.       (cond
  222.        ((not (= (param-list.nrequired param-list)
  223.         (generic-function-data.nrequired data)))
  224.     "required argument count mismatch")
  225.        ((and (not (or (generic-function-data.rest? data)
  226.               (generic-function-data.keys data)))
  227.          (or (param-list.rest? param-list)
  228.          (param-list.keys param-list)))
  229.     "generic function doesn't allow rest/keys")
  230.        ((and (or (generic-function-data.rest? data)
  231.          (generic-function-data.keys data))
  232.          (not (or (param-list.rest? param-list)
  233.               (param-list.keys param-list))))
  234.     "generic function requires rest/keys")
  235.        (else #F)))
  236.     (cond ((generic-function-data.read-only? data)
  237.        (dylan-call dylan:error
  238.                "add-method -- generic function is read-only"
  239.                generic-function))
  240.       ((and (pair? (generic-function-data.keys data))
  241.         (not (let ((method-keys (param-list.keys param-list)))
  242.                (or (param-list.rest? param-list)
  243.                (and method-keys (not (pair? method-keys)))
  244.                (subset? (generic-function-data.keys data)
  245.                     method-keys)))))
  246.        (dylan-call dylan:error
  247.                "add-method -- generic function requires certain keys"
  248.                (generic-function-data.keys data)))
  249.       ((congruency-error)
  250.        =>
  251.        (lambda (specific-error)
  252.          (dylan-call dylan:error
  253.              (string-append
  254.               "add-method -- parameter lists not congruent, "
  255.               specific-error)
  256.              generic-function method))))
  257.     (let ((old-method (find-method generic-function
  258.                    (method.specializers method))))
  259.       (if old-method
  260.       (delete-method! generic-function old-method))
  261.       (set-generic-function-data.methods!
  262.        data (cons method (generic-function-data.methods data)))
  263.       (if (not (null? multi-value-receiver))
  264.       ((car multi-value-receiver) method old-method)
  265.       old-method))))
  266.  
  267. (define (dylan::generic-function? obj)
  268.   (if (OneD-table/get *generic-function-data* obj #F) #T #F))
  269.  
  270. (define (dylan::create-generic-function name nrequired keys rest?)
  271.   (letrec ((data
  272.         (make-generic-function-data name
  273.                     (guarantee-integer nrequired)
  274.                     (guarantee-keys keys)
  275.                     (guarantee-boolean rest?)
  276.                     '() #F))
  277.        (generic-function
  278.         (lambda args
  279.           (generic-dispatch (car args) (cddr args) generic-function data))))
  280.     (OneD-table/put! *generic-function-data* generic-function data)
  281.     generic-function))
  282.  
  283. ;;;; Generic Dispatch
  284.  
  285. (define (generic-dispatch multiple-values original-args generic-function data)
  286.   (let ((nreq (generic-function-data.nrequired data))
  287.     (ngiven (length original-args)))
  288.     (if (> nreq ngiven)
  289.     (dylan-call dylan:error
  290.             "generic-dispatch -- too few arguments supplied"
  291.             nreq original-args))
  292.     (let ((applicable-methods
  293.        (sorted-applicable-methods
  294.         (generic-function-data.methods data)
  295.         original-args))
  296.       (non-req-args (but-first nreq original-args)))
  297.       (if (not (pair? applicable-methods))
  298.       (dylan-call dylan:error
  299.               "generic-dispatch -- no applicable methods"
  300.               generic-function original-args))
  301.       (if (> ngiven nreq)        ; More supplied than required
  302.       (if (or (generic-function-data.keys data)
  303.           (generic-function-data.rest? data))
  304.           (check-handled-keywords non-req-args applicable-methods)
  305.           (dylan-call dylan:error
  306.               "generic-dispatch -- too many arguments supplied"
  307.               generic-function nreq original-args)))
  308.       (let next-method-loop ((remaining-methods applicable-methods)
  309.                  (multiple-values multiple-values)
  310.                  (current-args original-args))
  311.     (apply (car remaining-methods)
  312.            multiple-values
  313.            (if (null? (cdr remaining-methods))
  314.            #F
  315.            (lambda (multiple-values next-method . these-args)
  316.              next-method    ; Ignored
  317.              (next-method-loop (cdr remaining-methods)
  318.                        multiple-values
  319.                        (if (null? these-args)
  320.                        current-args
  321.                        these-args))))
  322.            current-args)))))
  323.  
  324. (define (check-handled-keywords non-req-args methods)
  325.   ;;   gather the keywords for all of the applicable methods
  326.   ;;   if ALL methods specify !rest without !key then the call
  327.   ;;      is allowable
  328.   ;;   if ANY method specifies !rest (or !key with no specific
  329.   ;;      keys), then the call is allowable provided the extra
  330.   ;;      arguments are in keyword/value format
  331.   ;;   otherwise all of the keywords passed must be accepted by
  332.   (define all-!rest? #T)
  333.   (define any-!key? #F)
  334.   (let loop ((keywords '())
  335.          (methods methods))
  336.     (if (pair? methods)
  337.     (let* ((param-list (method.param-list (car methods)))
  338.            (keys (param-list.keys param-list))
  339.            (rest? (param-list.rest? param-list)))
  340.       (if (or (not rest?) keys) (set! all-!rest? #F))
  341.       (cond ((or (param-list.rest? param-list)
  342.              (and keys (not (pair? keys))))
  343.          (set! any-!key? #T))
  344.         ((pair? keys)
  345.          (loop (append keys keywords)
  346.                (cdr methods)))))
  347.     (cond (all-!rest? 'OK)
  348.           (any-!key? (dylan::keyword-validate #T non-req-args #T))
  349.           (else (dylan::keyword-validate #T non-req-args keywords))))))
  350.  
  351. ;;;; Finding and sorting applicable methods.
  352.  
  353. (define (sorted-applicable-methods methods arguments)
  354.   (map cdr                ; Strip specificities.
  355.        (sort (find-applicable-method-specificities methods arguments)
  356.          (lambda (specificities/method-1 specificities/method-2)
  357.            ;; Specificities are handled left-to-right through the list.
  358.            (let loop ((specificities-1 (car specificities/method-1))
  359.               (specificities-2 (car specificities/method-2)))
  360.          (if (and (pair? specificities-1)
  361.               (pair? specificities-2))
  362.              (let ((specificity-1 (car specificities-1))
  363.                (specificity-2 (car specificities-2)))
  364.                (cond ((eq? specificity-1 specificity-2)
  365.                   (loop (cdr specificities-1)
  366.                     (cdr specificities-2)))
  367.                  ((eq? #T specificity-1) #T)
  368.                  ((eq? #T specificity-2) #F)
  369.                  ((> specificity-1 specificity-2) #T)
  370.                  ((< specificity-1 specificity-2) #F)
  371.                  (else
  372.                   (loop (cdr specificities-1)
  373.                     (cdr specificities-2)))))
  374.              #T))))))
  375.  
  376. (define (find-applicable-method-specificities methods arguments)
  377.   ;; Returns a list of (specificities . method) for each applicable method
  378.   ;; in `methods'.  `specificities' is a list containing the specificity of
  379.   ;; each specializer of the method.  If there are no required arguments,
  380.   ;; `specificities' is always the empty list.  If there are no applicable
  381.   ;; methods, the return value is an empty list.
  382.   (let loop ((specificities/method-pairs '())
  383.          (methods methods))
  384.     (if (not (pair? methods))
  385.     specificities/method-pairs
  386.     (let ((method (car methods)))
  387.       (let ((specificities (method-applicable? method arguments)))
  388.         (loop (cond ((eq? #F specificities)
  389.              specificities/method-pairs)
  390.             ((eq? #T specificities)
  391.              (cons (cons '() method) specificities/method-pairs))
  392.             (else (cons (cons specificities method)
  393.                     specificities/method-pairs)))
  394.           (cdr methods)))))))
  395.  
  396. (define (method-applicable? method arguments)
  397.   ;; Returns #F if `method' shouldn't be applied to `arguments'.  Else,
  398.   ;; returns a list of the specificities of the specializers involved in
  399.   ;; the match.  If this list would be empty because there are no required
  400.   ;; parameters, return #T instead.
  401.   (let loop ((remaining-arguments arguments)
  402.          (remaining-specializers (method.specializers method))
  403.          (specificities '()))
  404.     (if (pair? remaining-specializers)
  405.     (if (not (pair? remaining-arguments))
  406.         (dylan-call dylan:error
  407.             "method-applicable? -- too few arguments"
  408.             arguments method)
  409.         (let ((specificity
  410.            (match-specializer? (car remaining-arguments)
  411.                        (car remaining-specializers))))
  412.           (if specificity
  413.           (loop (cdr remaining-arguments) (cdr remaining-specializers)
  414.             (cons specificity specificities))
  415.           #F)))
  416.     ;; MIT-Scheme bogosity.  (eq? #F '()) => #T!!!
  417.     (if (null? specificities)
  418.         #T
  419.         (reverse specificities)))))
  420.  
  421. (define (match-specializer? object specializer)
  422.   ;; Returns #F if `object' doesn't match `specializer'.  Else, returns the
  423.   ;; specificity of the match.  A high specificity indicates a very
  424.   ;; specific match.  A specificity of #t indicates an exact match of a
  425.   ;; singleton.
  426.   (cond ((singleton? specializer)
  427.      (if (Id? object (singleton.object specializer))
  428.          #T
  429.          #F))
  430.     ((class? specializer)
  431.      (if (subclass? (get-type object) specializer)
  432.          (class.specificity specializer)
  433.          #F))
  434.     (else (dylan-call dylan:error
  435.               "match-specializer? -- weird specializer"
  436.               specializer))))
  437.  
  438. (define (specializers=? specializers1 specializers2)
  439.   ;; Returns #T when two lists of specializers contain the same specializer
  440.   ;; in each position.  "same" is `eq?' except for singletons, which are
  441.   ;; not guaranteed to be unique for arbitrary objects (e.g. `3').
  442.   (let loop ((specs1 specializers1)
  443.          (specs2 specializers2))
  444.     length args)
  445.       ((0) (dylan-fn multi-value next-method))
  446.       ((1) (dylan-fn multi-value next-method (car args)))
  447.       ((2) (dylan-fn multi-value next-method (car args) (cadr args)))
  448.       ((3) (dylan-fn multi-value next-method
  449.              (car args) (cadr args) (caddr args)))
  450.       ((4) (dylan-fn multi-value next-method
  451.              (car args) (cadr args) (caddr args) (cadddr args)))
  452.       (else (apply dylan-fn multi-value next-method args)))))
  453.  
  454. (define (dylan-mv-apply dylan-fn multi-value . args)
  455.   ;; You must specify the first argument (multiple-values) explictly.
  456.   (let ((args (reformat-apply-args args)))
  457.     (case (length args)
  458.       ((0) (dylan-fn multi-value NEXT-METHOD:NOT-GENERIC))
  459.       ((1) (dylan-fn multi-value NEXT-METHOD:NOT-GENERIC (car args)))
  460.       ((2) (dylan-fn multi-value NEXT-METHOD:NOT-GENERIC (car args)
  461.              (cadr args)))
  462.       ((3) (dylan-fn multi-value NEXT-METHOD:NOT-GENERIC
  463.              (car args) (cadr args) (caddr args)))
  464.       ((4) (dylan-fn multi-value NEXT-METHOD:NOT-GENERIC
  465.              (car args) (cadr args) (caddr args) (cadddr args)))
  466.       (else (apply dylan-fn multi-value NEXT-METHOD:NOT-GENERIC args)))))
  467.  
  468. (define (dylan::function->method param-list scheme-function)
  469.   (dylan::make-method
  470.    param-list
  471.    (let ((nreq (param-list.nrequired param-list))
  472.      (rest? (param-list.rest? param-list))
  473.      (keys (param-list.keys param-list)))
  474.      (make-dylan-callable
  475.       scheme-function
  476.       (if (or rest? keys)
  477.       -1                ; Unknown number of arguments
  478.       nreq)))))
  479.  
  480. (define (dylan::dylan-callable->method param-list dylan-callable)
  481.   (dylan::make-method param-list dylan-callable))
  482.